package com.letsiot.applications.service.impl;

import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.letsiot.applications.dto.SysUserDTO;
import com.letsiot.applications.entity.SysOperatorLog;
import com.letsiot.applications.entity.SysRole;
import com.letsiot.applications.entity.SysUser;
import com.letsiot.applications.entity.SysUserAuths;
import com.letsiot.applications.entity.SysUserRole;
import com.letsiot.applications.entity.enums.IdentityType;
import com.letsiot.applications.entity.enums.LoginType;
import com.letsiot.applications.entity.enums.OnlineStatus;
import com.letsiot.applications.entity.enums.OperatorType;
import com.letsiot.applications.entity.enums.UserStatus;
import com.letsiot.applications.repository.SysOperatorLogMapper;
import com.letsiot.applications.repository.SysRoleMapper;
import com.letsiot.applications.repository.SysUserAuthsMapper;
import com.letsiot.applications.repository.SysUserMapper;
import com.letsiot.applications.repository.SysUserRoleMapper;
import com.letsiot.applications.service.SysUserService;
import com.letsiot.common.data.IdUtil;
import com.letsiot.common.util.RandomUtil;

@Service
@Transactional
public class SysUserServiceImpl implements SysUserService {

    private static final int PASSWORD_SALT_LENGTH = 8;

    private static final Long DEFAULT_ROLE_ID = 1L;

    @Value("${info.systemId.name}")
    private String systemName;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysUserAuthsMapper sysUserAuthsMapper;

    @Autowired
    private SysRoleMapper haloSysRoleMapper;

    @Autowired
    private SysUserRoleMapper haloSysUserRoleMapper;

    @Autowired
    private SysOperatorLogMapper haloSysOperatorLogMapper;

    @Override
    public int save(SysUser entity) {
        if (entity.getId() == null) {
            entity.setId(IdUtil.getId());
        }
        return sysUserMapper.insertSelective(entity);
    }

    @Override
    public int delete(Long id) {
        return sysUserMapper.deleteByPrimaryKey(id);
    }

    @Override
    public int update(SysUser entity) {
        return sysUserMapper.updateByPrimaryKeySelective(entity);
    }

    @Override
    public SysUser findById(Long id) {
        return sysUserMapper.selectByPrimaryKey(id);
    }

    @Override
    public List<SysUser> getAll() {
        return sysUserMapper.getAll();
    }

    @Override
    public SysUser getUserByIdentifier(String identifier) {
        SysUserAuths sysUserAuths = sysUserAuthsMapper.findByIdentifier(identifier);
        if (sysUserAuths == null) {
            return null;
        }
        return sysUserMapper.selectByPrimaryKey(sysUserAuths.getUserId());
    }

    @Override
    public SysUserAuths getUserAuthByIdentifier(String identifier) {
        return sysUserAuthsMapper.findByIdentifier(identifier);
    }

    @Override
    public List<SysUserAuths> getUserAuthByUserId(Long userId) {
        return sysUserAuthsMapper.findByUserId(userId);
    }

    @Override
    public Page<SysUser> getUserPage(Pageable pageable) {
        return sysUserMapper.getUserPage(pageable);
    }

    @Override
    public void createUser(SysUserDTO userVO) {
        SysUser user = new SysUser();
        user.setId(IdUtil.getId());
        user.setUserName(userVO.getUserName());
        user.setEmail(userVO.getEmail());
        user.setMobilePhone(userVO.getMobile());
        user.setUserType(userVO.getUserType());
        user.setStatus(UserStatus.INIT);
        sysUserMapper.insertSelective(user);
        PasswordEncoder passwordEncoder = new StandardPasswordEncoder();
        if (!StringUtils.isEmpty(userVO.getLoginName())) {
            SysUserAuths auths = new SysUserAuths();
            auths.setId(IdUtil.getId());
            auths.setIdentifier(userVO.getLoginName());
            auths.setUserId(user.getId());
            auths.setIdentityType(IdentityType.USER_NAME);
            auths.setFactor(RandomUtil.getRandomString(PASSWORD_SALT_LENGTH));
            auths.setCredential(passwordEncoder.encode(buildPassword(userVO.getPassword(), auths.getFactor())));
            sysUserAuthsMapper.insertSelective(auths);
        }
        if (!StringUtils.isEmpty(userVO.getEmail())) {
            SysUserAuths auths = new SysUserAuths();
            auths.setId(IdUtil.getId());
            auths.setIdentifier(userVO.getEmail());
            auths.setUserId(user.getId());
            auths.setIdentityType(IdentityType.EMAIL);
            auths.setFactor(RandomUtil.getRandomString(PASSWORD_SALT_LENGTH));
            auths.setCredential(passwordEncoder.encode(buildPassword(userVO.getPassword(), auths.getFactor())));
            sysUserAuthsMapper.insertSelective(auths);
        }
        if (!StringUtils.isEmpty(userVO.getMobile())) {
            SysUserAuths auths = new SysUserAuths();
            auths.setId(IdUtil.getId());
            auths.setIdentifier(userVO.getMobile());
            auths.setUserId(user.getId());
            auths.setIdentityType(IdentityType.MOBILE);
            auths.setFactor(RandomUtil.getRandomString(PASSWORD_SALT_LENGTH));
            auths.setCredential(passwordEncoder.encode(buildPassword(userVO.getPassword(), auths.getFactor())));
            sysUserAuthsMapper.insertSelective(auths);
        }
        if (userVO.getRoleId() != null) {
            SysRole role = haloSysRoleMapper.selectByPrimaryKey(userVO.getRoleId());
            if (role != null) {
                SysUserRole userRole = new SysUserRole();
                userRole.setId(IdUtil.getId());
                userRole.setUserId(user.getId());
                userRole.setRoleId(userVO.getRoleId());
                haloSysUserRoleMapper.insertSelective(userRole);
            }
        } else {
            SysUserRole userRole = new SysUserRole();
            userRole.setId(IdUtil.getId());
            userRole.setUserId(user.getId());
            userRole.setRoleId(DEFAULT_ROLE_ID);
            haloSysUserRoleMapper.insertSelective(userRole);
        }
    }

    @Override
    public boolean login(SysUser user) {
        SysUser updUser = new SysUser();
        updUser.setId(user.getId());
        updUser.setLoginIp(user.getLoginIp());
        updUser.setLoginTime(user.getLoginTime());
        updUser.setOnlineStatus(OnlineStatus.ONLINE);
        sysUserMapper.updateByPrimaryKeySelective(updUser);
        SysOperatorLog operatorLog = new SysOperatorLog();
        operatorLog.setId(IdUtil.getId());
        operatorLog.setUserId(user.getId());
        operatorLog.setUserName(user.getUserName());
        operatorLog.setOperatorType(OperatorType.LOGIN);
        operatorLog.setSystemName(systemName);
        operatorLog.setOperatorName(LoginType.LOGIN.getCode());
        operatorLog.setOperatorIp(user.getLoginIp());
        operatorLog.setCreateTime(new Date());
        haloSysOperatorLogMapper.insert(operatorLog);
        return true;
    }

    @Override
    public boolean logout(SysUser user) {
        SysUser updUser = new SysUser();
        updUser.setId(user.getId());
        updUser.setOnlineStatus(OnlineStatus.OFFLINE);
        sysUserMapper.updateByPrimaryKeySelective(updUser);
        SysOperatorLog operatorLog = new SysOperatorLog();
        operatorLog.setId(IdUtil.getId());
        operatorLog.setUserId(user.getId());
        operatorLog.setUserName(user.getUserName());
        operatorLog.setOperatorType(OperatorType.LOGIN);
        operatorLog.setSystemName(systemName);
        operatorLog.setOperatorName(LoginType.LOGOUT.getCode());
        operatorLog.setOperatorIp(user.getLoginIp());
        operatorLog.setCreateTime(new Date());
        haloSysOperatorLogMapper.insert(operatorLog);
        return true;
    }

    private String buildPassword(String password, String factor) {
        return new StringBuffer().append(password).append(factor).toString();
    }

    @Override
    public int updatePassword(SysUserDTO userVO) {
        List<SysUserAuths> userAuthList = sysUserAuthsMapper.findByUserId(userVO.getUserId());
        PasswordEncoder passwordEncoder = new StandardPasswordEncoder();
        for (SysUserAuths ua : userAuthList) {
            ua.setFactor(RandomUtil.getRandomString(8));
            ua.setCredential(passwordEncoder.encode(buildPassword(userVO.getPassword(), ua.getFactor())));
        }
        return 0;
    }
}
